////
//
/// \file   ./src/operation_node.cpp
//
//  Copyright 2007, 2008 Chandler Carruth
//
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License.  You may obtain a copy
//  of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
//  License for the specific language governing permissions and limitations
//  under the License.
//
////

#include <inc/parser/operation_node.hpp>

#include <string>

#include <boost/scoped_ptr.hpp>

#include <inc/parser/expression_node.hpp>
#include <inc/parser/parse_tree_node.hpp>
#include <inc/parser/label_node.hpp>
#include <inc/parser/symbol_node.hpp>
#include <inc/parser/ws_parser.hpp>

namespace inc {
namespace parser {

using std::string;
using boost::scoped_ptr;

/// \brief  Basic constructor
/// \author Chandler Carruth
/// \date   2008.05.23

OperationNode::OperationNode()
    : ws( new WSParser )
{}

/// \brief  Basic destructor
/// \author Chandler Carruth
/// \date   2008.05.23

OperationNode::~OperationNode()
{}

/// \brief  Parse compound expression terminator
/// \author Chandler Carruth
/// \date   2007.06.28
///
/// This method parses out a compound expression's terminator

bool
OperationNode::parse_terminator( const iterator& first,
                                 const iterator& last,
                                 iterator& last_parsed )
{
  iterator current = first;
  if( ws->parse_ws( current, last, last_parsed ) )
    current = last_parsed;

  if( *current == ')' )
  {
    last_parsed = ++current;
    return true;
  }

  return false;
}


/// \brief  Parse compound expression predicate expr
/// \author Chandler Carruth
/// \date   2007.06.28
///
/// This method parses out a compound expression's predicate expression. Only
/// valid for compound expressions involving binary operations and infix
/// operators.

bool
OperationNode::parse_rhs_expr( const iterator& first,
                               const iterator& last,
                               iterator& last_parsed )
{
  scoped_ptr<ExpressionNode> expr( new ExpressionNode );
  if( expr->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;

    if( parse_terminator( current, last, last_parsed ) )
    {
      rhs_expr.swap( expr );
      return true;
    }
  }

  last_parsed = first;
  return false;
}

/// \brief  Parse unary prefix symbol operator
/// \author Chandler Carruth
/// \date   2007.06.28
///
/// This method parses out a operation composed of a unary prefix symbol
/// operator and its operand.

bool
OperationNode::parse_prefix_symbol( const iterator& first,
                                    const iterator& last,
                                    iterator& last_parsed )
{
  scoped_ptr<SymbolNode> symbol( new SymbolNode );
  if( symbol->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;

    if( ws->parse_ws( current, last, last_parsed ) )
      current = last_parsed;

    if( parse_rhs_expr( current, last, last_parsed ) )
    {
      op_symbol.swap( symbol );
      return true;
    }
  }

  last_parsed = first;
  return false;
}

/// \brief  Parse compound double label
/// \author Chandler Carruth
/// \date   2007.06.28
///
/// This method parses out a compound expression composed of a double label.
/// This is a syntactically ambiguous, but semantically well defined construct,
/// so we just force a recognition in the parse tree, and we'll fix it during
/// semantic processing.

bool
OperationNode::parse_label_label( const iterator& first,
                                  const iterator& last,
                                  iterator& last_parsed )
{
  scoped_ptr<LabelNode> lhs( new LabelNode );
  if( lhs->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;

    if( ws->parse_ws( current, last, last_parsed ) )
    {
      current = last_parsed;

      scoped_ptr<LabelNode> rhs( new LabelNode );
      if( rhs->parse( current, last, last_parsed ) )
      {
        current = last_parsed;

        if( parse_terminator( current, last, last_parsed ) )
        {
          lhs_label.swap( lhs );
          rhs_label.swap( rhs );
          return true;
        }
      }
    }
  }

  last_parsed = first;
  return false;
}

/// \brief  Parse a prefix unary label operation
/// \author Chandler Carruth
/// \date   2007.06.28
///
/// This method parses out a compound expression composed of a prefix label
/// operator.

bool
OperationNode::parse_prefix_label( const iterator& first,
                                   const iterator& last,
                                   iterator& last_parsed )
{
  scoped_ptr<LabelNode> label( new LabelNode );
  if( label->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;

    if( ws->parse_ws( current, last, last_parsed ) )
    {
      current = last_parsed;

      if( parse_rhs_expr( current, last, last_parsed ) )
      {
        op_label.swap( label );
        return true;
      }
    }
  }

  last_parsed = first;
  return false;
}

/// \brief  Parse compound expression w/ postfix or binary label
/// \author Chandler Carruth
/// \date   2007.06.28
///
/// This method parses out a compound expression which forms a postfix or
/// binary label operator. The first expression is already parsed by the time
/// this method gets the baton...

bool
OperationNode::parse_postfix_binary_label( const iterator& first,
                                           const iterator& last,
                                           iterator& last_parsed )
{
  scoped_ptr<LabelNode> label( new LabelNode );
  if( label->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;
    op_label.swap( label );

    //  Try to finish the expression first, this is an easy parse.
    if( this->parse_terminator( current, last, last_parsed ) )
      return true;

    if( ws->parse_ws( current, last, last_parsed ) )
    {
      current = last_parsed;

      if( this->parse_rhs_expr( current, last, last_parsed ) )
        return true;
    }
  }

  op_label.reset();
  last_parsed = first;
  return false;
}


/// \brief  Parse compound expression w/ postfix or binary symbol
/// \author Chandler Carruth
/// \date   2007.06.28
///
/// This method parses out a compound expression which forms a postfix or
/// binary symbol operator. The first expression is already parsed by the time
/// this method gets the baton...

bool
OperationNode::parse_postfix_binary_symbol( const iterator& first,
                                            const iterator& last,
                                            iterator& last_parsed )
{
  scoped_ptr<SymbolNode> symbol( new SymbolNode );
  if( symbol->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;
    op_symbol.swap( symbol );

    //  Try to finish the expression first, this is an easy parse.
    if( this->parse_terminator( current, last, last_parsed ) )
      return true;

    if( ws->parse_ws( current, last, last_parsed ) )
      current = last_parsed;

    //  Parse a rhs expression
    if( this->parse_rhs_expr( current, last, last_parsed ) )
      return true;
  }

  op_symbol.reset();
  last_parsed = first;
  return false;
}


/// \brief  Parse compound expression w/ expr prefix
/// \author Chandler Carruth
/// \date   2007.06.28
///
/// This method parses out a compound expression which begins with an
/// expression. It then branches off to handle postfix operators and binary
/// operators. It stores the expression as a LHS expression.

bool
OperationNode::parse_lhs_expr( const iterator& first,
                               const iterator& last,
                               iterator& last_parsed )
{
  scoped_ptr<ExpressionNode> expr( new ExpressionNode );
  if( expr->parse( first, last, last_parsed ) )
  {
    iterator current = last_parsed;
    lhs_expr.swap( expr );

    //  skip any whitespace
    if( ws->parse_ws( current, last, last_parsed ) )
    {
      current = last_parsed;

      //  since we found white space, we can check for a label operator
      if( this->parse_postfix_binary_label( current, last, last_parsed ) )
        return true;
    }

    //  try and parse out a symbol operator since we don't have a label
    //  operator.
    if( this->parse_postfix_binary_symbol( current, last, last_parsed ) )
      return true;
  }

  lhs_expr.reset();
  last_parsed = first;
  return false;
}

/// \brief  Parse an grouped operation
/// \author Chandler Carruth
/// \date   2007.06.27
///
/// This parses out a grouped operation. It is delimited by ()s, and can be
/// made up of several different compound constructs.

bool
OperationNode::parse( const iterator& first,
                      const iterator& last,
                      iterator& last_parsed )
{
  assert( get_text().empty() );
  assert( !op_label );
  assert( !op_symbol );
  assert( !lhs_expr );
  assert( !rhs_expr );
  assert( !lhs_label );
  assert( !rhs_label );

  iterator current = first;

  //  all operations begin with an opening parenthesis
  if( *current == '(' )
  {
    ++current;
    //  zap any whitespace
    if( ws->parse_ws( current, last, last_parsed ) )
      current = last_parsed;

    //  start by checking for our easy-out

    //  No go, so we parse it by hand... first thing we try are the two
    //  compound exprs starting with a label. we check these first to ensure we
    //  grab any labels out before turning them into "expressions". These
    //  labels may need to derive from label, or from name, and we won't know
    //  until a semantic pass. Until then we check early for ambiguous
    //  "labels"...

    //  We aren't in the ambiguous ( label label ) situation, so we now get to
    //  try and parse exact versions of this expression

    //  We now know we start with an expression, and all other possibilities
    //  branch off of that.
    if( parse_prefix_symbol( current, last, last_parsed ) ||
        parse_label_label( current, last, last_parsed ) ||
        parse_prefix_label( current, last, last_parsed ) ||
        parse_lhs_expr( current, last, last_parsed ) )
    {
      set_text( string( first, last_parsed ) );
      return true;
    }
  }

  last_parsed = first;
  return false;
}

/// \brief  Accessor for an ambiguous double labels' LHS label
/// \author Chandler Carruth
/// \date   2008.05.23

LabelNode*
OperationNode::get_lhs_label() const
{
  if( lhs_label != NULL && rhs_label != NULL )
    return lhs_label.get();

  return NULL;
}

/// \brief  Accessor for an ambiguous double labels' RHS label
/// \author Chandler Carruth
/// \date   2008.05.23

LabelNode*
OperationNode::get_rhs_label() const
{
  if( lhs_label != NULL && rhs_label != NULL )
    return rhs_label.get();

  return NULL;
}

/// \brief  Accessor for a unary operations' expression
/// \author Chandler Carruth
/// \date   2008.05.23

ExpressionNode*
OperationNode::get_unary_expr() const
{
  if( lhs_expr != NULL && rhs_expr == NULL )
    return lhs_expr.get();
  if( rhs_expr != NULL && lhs_expr == NULL )
    return rhs_expr.get();

  return NULL;
}

/// \brief  Tester for prefix unary operators
/// \author Chandler Carruth
/// \date   2008.05.23

bool
OperationNode::is_unary_prefix() const
{
  return lhs_expr == NULL && rhs_expr != NULL;
}

/// \brief  Tester for postfix unary operators
/// \author Chandler Carruth
/// \date   2008.05.23

bool
OperationNode::is_unary_postfix() const
{
  return lhs_expr != NULL && rhs_expr == NULL;
}

/// \brief  Accessor for a binary operations' LHS expression
/// \author Chandler Carruth
/// \date   2008.05.23

ExpressionNode*
OperationNode::get_lhs_expr() const
{
  if( lhs_expr != NULL && rhs_expr != NULL )
    return lhs_expr.get();

  return NULL;
}

/// \brief  Accessor for a binary operations' RHS expression
/// \author Chandler Carruth
/// \date   2008.05.23

ExpressionNode*
OperationNode::get_rhs_expr() const
{
  if( lhs_expr != NULL && rhs_expr != NULL )
    return rhs_expr.get();

  return NULL;
}

/// \brief  Accessor for a label operator
/// \author Chandler Carruth
/// \date   2008.05.23

LabelNode*
OperationNode::get_op_label() const
{
  return op_label.get();
}

/// \brief  Accessor for a symbol operator
/// \author Chandler Carruth
/// \date   2008.05.23

SymbolNode*
OperationNode::get_op_symbol() const
{
  return op_symbol.get();
}

} //  end parser namespace
} //  end inc namespace
